package org.example.mallweb.mallservice.service.serviceimpl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;

import lombok.extern.slf4j.Slf4j;
import org.example.mallcommon.constant.CollectConstant;
import org.example.mallcommon.constant.FavoriteConstant;
import org.example.mallcommon.myenum.ActionType;
import org.example.mallcommon.projo.*;

import org.example.mallcommon.result.ReturnResult;
import org.example.mallcommon.vio.ArticleVio;
import org.example.mallcommon.vio.ArticleVoPage;
import org.example.mallcommon.vo.*;
import org.example.mallweb.mallservice.mapperervice.taguservalue.TagUserScoreMapperImpl;
import org.example.mallweb.mallservice.mybatiscommon.articles.ArticleMapper;
import org.example.mallweb.mallservice.mybatiscommon.articletags.ArticleTagMapper;
import org.example.mallweb.mallservice.mybatiscommon.userfollow.UserFollowMapper;
import org.example.mallweb.mallservice.mybatiscommon.userprojo.UserMapper;
import org.example.mallweb.mallservice.service.*;

import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;


import javax.annotation.Resource;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author 韩先楚
 */
@Service
@Slf4j
public class ArticleServiceImpl implements ArticleService {

    @Resource
    private ArticleMapper articleMapper;
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private FavorityService favorityService;
    @Resource
    private CollectService collectService;
    @Resource
    private TagUserValueService tagUserValueService;
    @Resource
    private TagUserScoreMapperImpl tagUserScoreMapper;

    @Resource
    private UserMapper userMapper;
    @Resource
    private ArticleTagMapper articleTagMapper;
    @Resource
    private UserFollowMapper userFollowMapper;
    @Resource
    private ElasticSearchService elasticSearchService;


    //获取所有文章
    @Override
    public ReturnResult<ArticleUserVio> getAllArticle(Integer page, Integer size, Long category) {

        Page<ArticleUserVo> articleUserVoPage = articleMapper.selectArticleUserVoAllPage(new Page<ArticleUserVo>(page, size), category);
        ArticleUserVio articleUserVio = new ArticleUserVio();
        articleUserVio.setList(articleUserVoPage.getRecords());
        articleUserVio.setTotal(articleUserVoPage.getTotal());
        return ReturnResult.success(articleUserVio);
    }

    //获取最新文章
    @Override
    public ReturnResult<ArticleUserVio> getLatestArticle(Integer page, Integer size, Long category) {
        Page<ArticleUserVo> articleUserVoPage = articleMapper.selectArticleUserVoNewPage(new Page<ArticleUserVo>(page, size), category);
        ArticleUserVio articleUserVio = new ArticleUserVio();
        articleUserVio.setList(articleUserVoPage.getRecords());
        articleUserVio.setTotal(articleUserVoPage.getTotal());
        return ReturnResult.success(articleUserVio);
    }

    //获取点赞最多文章
    @Override
    public ReturnResult<ArticleUserVio> getMostPopularArticle(Integer page, Integer size, Long category) {
        Page<ArticleUserVo> articleUserVoPage = articleMapper.selectArticleUserVoFavPage(new Page<ArticleUserVo>(page, size), category);
        ArticleUserVio articleUserVio = new ArticleUserVio();
        articleUserVio.setList(articleUserVoPage.getRecords());
        articleUserVio.setTotal(articleUserVoPage.getTotal());
        return ReturnResult.success(articleUserVio);
    }

    @Override
    public ReturnResult<ArticleUserVio> getMostViewedArticle(Integer page, Integer size, Long category) {
        Page<ArticleUserVo> articleUserVoPage = articleMapper.selectArticleUserVoViewPage(new Page<ArticleUserVo>(page, size), category);
        ArticleUserVio articleUserVio = new ArticleUserVio();
        articleUserVio.setList(articleUserVoPage.getRecords());
        articleUserVio.setTotal(articleUserVoPage.getTotal());
        return ReturnResult.success(articleUserVio);
    }

    /**
     * 根据用户id获取用户发布和未发布的文章
     */
    @Override
    public ReturnResult<List<ArticleVo>> getMyArticle(Integer userId) {
        List<Article> articles = null;
        try {
            articles = articleMapper.selectList(new QueryWrapper<Article>().eq("user_id", userId).ne("status", 2));
        } catch (Exception e) {
            log.info(e.getMessage());
        }
        //将Article转换为ArticleVo
        List<ArticleVo> articleVos = BeanUtil.copyToList(articles, ArticleVo.class);
        return ReturnResult.success(articleVos);
    }

    //查找文章详情
    @Transactional(rollbackFor = Exception.class)
    public ReturnResult<ArticleDetailVo> selectArticleDetail(Long articleId, Integer userId) {
        //根据文章id查询文章
        Article article = articleMapper.selectById(articleId);
        if (article == null)
            throw new RuntimeException("文章不存在");
        ArticleDetailVo articleDetailVo = BeanUtil.copyProperties(article, ArticleDetailVo.class);
        //查询文章包含的标签信息
        List<Tag> articleTags = articleTagMapper.selectArticleTagByArticleId(articleId);
        articleDetailVo.setTags(articleTags);
        //查询作者信息
        User author = userMapper.selectById(articleDetailVo.getUserId());
        if (author != null) {
            articleDetailVo.setImage(author.getImage());
            articleDetailVo.setNickname(author.getNickName());
        }
        //判断用户是否收藏该文章
        articleDetailVo.setIsLike(favorityService.isFavority(userId, articleId));
        //判断用户是否点赞该文章
        articleDetailVo.setIsCollect(collectService.isCollect(userId, articleId));
        //查询redis中对于文章的点赞数和收藏数
        if (articleDetailVo.getIsLike() == 1)
            articleDetailVo.setLikeCount(articleDetailVo.getLikeCount() + 1);
        if (articleDetailVo.getIsCollect() == 1)
            articleDetailVo.setCollectCount(articleDetailVo.getCollectCount() + 1);
        //增加浏览量
        articleDetailVo.setViewCount(articleDetailVo.getViewCount() + 1);
        Article article1 = new Article();
        article1.setViewCount(articleDetailVo.getViewCount());
        articleMapper.update(article1, new QueryWrapper<Article>().eq("id", articleId));
        //增加浏览权重
        List<Long> list = articleDetailVo.getTags().stream().map(tag -> tag.getId()).collect(Collectors.toList());
        if (list.size() > 0)
            tagUserValueService.addTagValue(userId, list, ActionType.VIEW);
        return ReturnResult.success(articleDetailVo);
    }

    //分页获取推荐文章
    @Override
    public ReturnResult<ArticleUserVio> getRecommendArticle(Integer page, Integer size, Integer userId) {
        //获取各个标签的权重
        List<ScoreRule> scoreRules = tagUserScoreMapper.list(new QueryWrapper<ScoreRule>().eq("user_id", userId));
        return ReturnResult.success(recommendArticlesWithPagination(scoreRules, page, size));
    }

    //Todo  更新es索引
    //删除文章
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ReturnResult<String> deleteArticle(Long articleId) throws IOException {
        Article article = new Article();
        article.setStatus(2);
        articleMapper.update(article, new QueryWrapper<Article>().eq("id", articleId));
        //更新es索引
        elasticSearchService.deleteById(articleId);
        return ReturnResult.success("删除成功");
    }

    /**
     * 修改文章
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateArticle(ArticleVio articleVio) throws IOException {
        //1.修改文章
        Article article = BeanUtil.copyProperties(articleVio, Article.class);
        articleMapper.updateById(article);
        //2.修改标签
        articleTagMapper.delete(new QueryWrapper<ArticleTag>().eq("article_id", articleVio.getId()));
        articleTagMapper.insertListByArticleId(articleVio.getId(), articleVio.getTags());
        saveOrUpdate(article);
    }

    /**
     * 新增或修改es索引
     */
    public void saveOrUpdate(Article article) throws IOException {
        SearchVo searchVo = BeanUtil.copyProperties(article, SearchVo.class);
        elasticSearchService.saveOrUpdate(searchVo);
    }

    //添加文章
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addArticle(ArticleVio articleVio) throws IOException {
        Article article = BeanUtil.copyProperties(articleVio, Article.class);
        articleMapper.insert(article);
        articleTagMapper.insertListByArticleId(article.getId(), articleVio.getTags());
        saveOrUpdate(article);
    }

    //获取热门文章
    @Override
    public ReturnResult<ArticleUserVio> getHotArticle(Integer page, Integer size, Integer userId) {
        Page<ArticleUserVo> list = articleMapper.selectHotArticle(new Page<ArticleUserVo>(page, size));
        ArticleUserVio articleUserVio = new ArticleUserVio();
        articleUserVio.setList(list.getRecords());
        articleUserVio.setTotal(list.getTotal());
        return ReturnResult.success(articleUserVio);
    }

    //获取最新文章
    @Override
    public ReturnResult<ArticleUserVio> getNewArticle(Integer page, Integer size, Integer userId) {
        Page<ArticleUserVo> list = articleMapper.selectNewArticle(new Page<ArticleUserVo>(page, size));
        ArticleUserVio articleUserVio = new ArticleUserVio();
        articleUserVio.setList(list.getRecords());
        articleUserVio.setTotal(list.getTotal());
        return ReturnResult.success(articleUserVio);
    }

    //获取关注文章
    @Override
    public ReturnResult<ArticleUserVio> getFollowArticle(Integer page, Integer size, Integer userId) {
        //获取关注的用户id
        List<Integer> followList = userFollowMapper.selectList(new QueryWrapper<UserFollow>().eq("user_id", userId).eq("status", 1))
                .stream()
                .map(userFollow -> userFollow.getFollowedId())
                .collect(Collectors.toList());
        ArticleUserVio articleUserVio = new ArticleUserVio();
        if (followList.size() == 0)
            return ReturnResult.success(articleUserVio);
        //分页获取文章
        Page<ArticleUserVo> list = articleMapper.selectFollowArticle(new Page<ArticleUserVo>(page, size), followList);
        articleUserVio.setList(list.getRecords());
        articleUserVio.setTotal(list.getTotal());
        return ReturnResult.success(articleUserVio);
    }


    //根据用户id分页获取用户发布的文章
    @Override
    public ReturnResult<ArticleVoPage> getArticleByUserId(Integer page, Integer size, Integer userId) {
        Page<ArticleVo> list = articleMapper.selectArticleByUserId(new Page<ArticleVo>(page, size), userId);
        ArticleVoPage articleVoPage = new ArticleVoPage();
        articleVoPage.setArticles(list.getRecords());
        articleVoPage.setCount(list.getTotal());
        return ReturnResult.success(articleVoPage);
    }


    private ArticleUserVio recommendArticlesWithPagination(List<ScoreRule> scoreRules, Integer page, Integer size) {
        //1.获取总权重,和各个标签的返回的个数
        BigDecimal reduce = scoreRules.stream().map(scoreRule -> scoreRule.getScoreValue())
                .reduce(new BigDecimal(0), (bigDecimal, bigDecimal2) -> bigDecimal.add(bigDecimal2));
        HashMap<Long, Integer> tagDistribute = new HashMap<>();
        int remaining = size;
        // 1.1先按权重比例分配整数部分
        for (ScoreRule rule : scoreRules) {
            BigDecimal ratio = rule.getScoreValue().divide(reduce, 4, BigDecimal.ROUND_HALF_UP);
            int count = ratio.multiply(new BigDecimal(size)).intValue();
            tagDistribute.put(rule.getTagId(), count);
            remaining -= count;
        }

        // 1.2处理剩余数量，按权重从高到低分配
        if (remaining > 0) {
            scoreRules.sort((r1, r2) -> r2.getScoreValue().compareTo(r1.getScoreValue()));
            for (ScoreRule rule : scoreRules) {
                if (remaining <= 0) break;
                tagDistribute.put(rule.getTagId(), tagDistribute.get(rule.getTagId()) + 1);
                remaining--;
            }
        }
        //2.根据各个标签的权重,分页获取文章
        List<ArticleUserVo> recommendedArticles = new ArrayList<>();
        for (Map.Entry<Long, Integer> entry : tagDistribute.entrySet()) {
            Long tagId = entry.getKey();
            int count = entry.getValue();
            if (count <= 0) continue;
            Page<ArticleUserVo> articleUserVoPage = articleMapper.selectRecommendByTagId(new Page<ArticleUserVo>(page, count), tagId);
            recommendedArticles.addAll(articleUserVoPage.getRecords());
        }
        //去重
        recommendedArticles = recommendedArticles.stream().distinct().collect(Collectors.toList());
        List<Long> list = recommendedArticles.stream().map(articleUserVo -> articleUserVo.getArticleId()).collect(Collectors.toList());
        //3.如果文章个数不足,则随机获取剩下文章
        if (recommendedArticles.size() < size) {
            int need = size - recommendedArticles.size();
            Page<ArticleUserVo> voPage = articleMapper.selectRandom(new Page<ArticleUserVo>(page, need), list);
            recommendedArticles.addAll(voPage.getRecords());
        }
        //4.获取总文章数
        Long count = articleMapper.selectCount(new QueryWrapper<Article>().eq("status", 1));
        ArticleUserVio articleUserVio = new ArticleUserVio();
        articleUserVio.setTotal(count);
        //5.将其打乱顺序,返回
        articleUserVio.setList(recommendedArticles);
        return articleUserVio;
    }


}
